﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Ooaavee.Events;
using System.Windows.Forms;
using System.Diagnostics;

namespace UnitTests.Ooaavee.Events
{
    /// <summary>
    /// Tests EventWatcher class.
    /// </summary>
    [TestClass]
    public class EventWatcherTest
    {
        /// <summary>
        /// Tests constructor.
        /// </summary>
        [TestMethod]
        public void ConstructorTest1()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            EventWatcherResults results;
            using (EventWatcher target = new EventWatcher(x, "Event3"))
            {
                x.RaiseFourEvents();
                results = target.GetResults();
            }

            Assert.AreEqual(1, results.EventCount, "Expected one event.");
            Assert.AreEqual("Event3", results.ReceivedEvents[0].Name, "Received an event we did not expect.");
            Assert.AreEqual(2, results.ReceivedEvents[0].Args.Count, "Event3: Invalid number of event args.");
            Assert.AreEqual(123, results.ReceivedEvents[0].Args[0], "Event3: The first argument should be 123.");
            Assert.AreEqual(456, results.ReceivedEvents[0].Args[1], "Event3: The second argument should be 456.");            
        }

        /// <summary>
        /// Tests constructor.
        /// </summary>
        [TestMethod]
        public void ConstructorTest2()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            EventWatcherResults results;
            using (EventWatcher target = new EventWatcher(x, "Event3", BindingFlags.Public | BindingFlags.Instance))
            {
                x.RaiseFourEvents();
                results = target.GetResults();
            }

            Assert.AreEqual(1, results.EventCount, "Expected one event.");
            Assert.AreEqual("Event3", results.ReceivedEvents[0].Name, "Received an event we did not expect.");
            Assert.AreEqual(2, results.ReceivedEvents[0].Args.Count, "Event3: Invalid number of event args.");
            Assert.AreEqual(123, results.ReceivedEvents[0].Args[0], "Event3: The first argument should be 123.");
            Assert.AreEqual(456, results.ReceivedEvents[0].Args[1], "Event3: The second argument should be 456.");
        }

        /// <summary>
        /// Tests constructor.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ConstructorTest3()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();
            using (EventWatcher target = new EventWatcher(x, "Event3", BindingFlags.NonPublic | BindingFlags.Instance))
            {
            }
        }

        /// <summary>
        /// Tests EventOccurred event.
        /// </summary>
        [TestMethod]
        public void EventOccurredTest1()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            List<EventWatcherEventArgs> events = new List<EventWatcherEventArgs>();

            using (EventWatcher target = new EventWatcher(x))
            {
                target.EventOccurred += delegate(object sender, EventWatcherEventArgs e)
                {
                    events.Add(e);
                };
                x.RaiseFourEvents();

                Assert.AreEqual(4,
                                events.Count,
                                "Invalid number of events.");

                // EventHandler(object sender, System.EventArgs e)
                Assert.AreEqual("Event1",
                                events[0].Name,
                                "Received an unexpected event.");
                Assert.AreEqual(2,
                                events[0].Args.Count,
                                "Event1: Invalid number of event args.");
                Assert.AreEqual(x,
                                events[0].Args[0],
                                "Event1: The first argument should be sender.");
                Assert.AreEqual(EventArgs.Empty,
                                events[0].Args[1],
                                "The second argument should be EventArgs.Empty.");

                // MyCustomEventHandler(object sender, MyCustomEventArgs e)
                Assert.AreEqual("Event2",
                                events[1].Name,
                                "Received an unexpected event.");
                Assert.AreEqual(2,
                                events[1].Args.Count,
                                "Event2: Invalid number of event args.");
                Assert.AreEqual(x,
                                events[1].Args[0],
                                "Event2: The first argument should be sender.");
                Assert.AreEqual(typeof (SomeClassThatRaisesEvents.MyCustomEventArgs),
                                events[1].Args[1].GetType(),
                                "Event2: The second argument should be MyCustomEventArgs.");
                Assert.AreEqual("Hello Unit Test!",
                                ((SomeClassThatRaisesEvents.MyCustomEventArgs) events[1].Args[1]).Message,
                                "Event2: Invalid message.");

                // MyAnotherCustomEventHandler(int x, int y)
                Assert.AreEqual("Event3",
                                events[2].Name,
                                "Received an unexpected event.");
                Assert.AreEqual(2,
                                events[2].Args.Count,
                                "Event3: Invalid number of event args.");
                Assert.AreEqual(123,
                                events[2].Args[0],
                                "Event3: The first argument should be 123.");
                Assert.AreEqual(456,
                                events[2].Args[1],
                                "Event3: The second argument should be 456.");

                // MyThirdCustomEventHandler()
                Assert.AreEqual("Event4",
                                events[3].Name,
                                "Received an unexpected event.");
                Assert.AreEqual(0,
                                events[3].Args.Count,
                                "Event4: Invalid number of event args.");
            }
        }

        /// <summary>
        /// Tests EventOccurred event.
        /// </summary>
        [TestMethod]
        public void EventOccurredTest2()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            List<object> senders = new List<object>();
            List<EventWatcherEventArgs> events = new List<EventWatcherEventArgs>();

            using (EventWatcher target = new EventWatcher(x))
            {
                target.EventOccurred += delegate(object sender, EventWatcherEventArgs e)
                {
                    senders.Add(sender);
                    events.Add(e);
                };
                x.RaiseFourEvents();

                Assert.AreEqual(4, events.Count, "Invalid number of events.");
                Assert.AreEqual(4, senders.Count, "Invalid number of events.");
                Assert.AreEqual(target, senders[0], "Sender should be the EventWatcher.");
                Assert.AreEqual(target, senders[1], "Sender should be the EventWatcher.");
                Assert.AreEqual(target, senders[2], "Sender should be the EventWatcher.");
                Assert.AreEqual(target, senders[3], "Sender should be the EventWatcher.");

                // EventHandler(object sender, System.EventArgs e)
                Assert.AreEqual("Event1", events[0].Name, "Received an unexpected event.");
                Assert.AreEqual(2, events[0].Args.Count, "Event1: Invalid number of event args.");
                Assert.AreEqual(x, events[0].Args[0], "Event1: The first argument should be sender.");
                Assert.AreEqual(EventArgs.Empty, events[0].Args[1], "The second argument should be EventArgs.Empty.");

                // MyCustomEventHandler(object sender, MyCustomEventArgs e)
                Assert.AreEqual("Event2", events[1].Name, "Received an unexpected event.");
                Assert.AreEqual(2, events[1].Args.Count, "Event2: Invalid number of event args.");
                Assert.AreEqual(x, events[1].Args[0], "Event2: The first argument should be sender.");
                Assert.AreEqual(typeof(SomeClassThatRaisesEvents.MyCustomEventArgs), events[1].Args[1].GetType(), "Event2: The second argument should be MyCustomEventArgs.");
                Assert.AreEqual("Hello Unit Test!", ((SomeClassThatRaisesEvents.MyCustomEventArgs)events[1].Args[1]).Message, "Event2: Invalid message.");

                // MyAnotherCustomEventHandler(int x, int y)
                Assert.AreEqual("Event3", events[2].Name, "Received an unexpected event.");
                Assert.AreEqual(2, events[2].Args.Count, "Event3: Invalid number of event args.");
                Assert.AreEqual(123, events[2].Args[0], "Event3: The first argument should be 123.");
                Assert.AreEqual(456, events[2].Args[1], "Event3: The second argument should be 456.");

                // MyThirdCustomEventHandler()
                Assert.AreEqual("Event4", events[3].Name, "Received an unexpected event.");
                Assert.AreEqual(0, events[3].Args.Count, "Event4: Invalid number of event args.");
            }
        }

        /// <summary>
        /// Tests GetResults method.
        /// </summary>
        [TestMethod]
        public void GetResultsTest1()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            EventWatcherResults results;
            using (EventWatcher target = new EventWatcher(x))
            {
                x.RaiseFourEvents();
                results = target.GetResults();
            }

            Assert.AreEqual(4, results.EventCount, "Expected four events.");
            Assert.AreEqual("Event1", results.ReceivedEvents[0].Name, "Received an event we did not expect.");
            Assert.AreEqual("Event2", results.ReceivedEvents[1].Name, "Received an event we did not expect.");
            Assert.AreEqual("Event3", results.ReceivedEvents[2].Name, "Received an event we did not expect.");
            Assert.AreEqual("Event4", results.ReceivedEvents[3].Name, "Received an event we did not expect.");
        }

        /// <summary>
        /// Tests GetResults method.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ObjectDisposedException))]
        public void GetResultsTest2()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            EventWatcher target;
            using (target = new EventWatcher(x))
            {
            }
            target.GetResults();
        }

        /// <summary>
        /// Tests Suspend method.
        /// </summary>
        [TestMethod]
        public void SuspendTest1()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            using (EventWatcher target = new EventWatcher(x))
            {
                int eventCount = 0;
                target.EventOccurred += delegate(object sender, EventWatcherEventArgs e)
                {
                    eventCount++;
                };

                x.RaiseFourEvents();
                target.Suspend();
                x.RaiseFourEvents();

                Assert.AreEqual(4, eventCount, "Invalid number of events.");
            }            
        }

        /// <summary>
        /// Tests Suspend method.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void SuspendTest2()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            using (EventWatcher target = new EventWatcher(x))
            {
                target.Suspend();
                target.Suspend();
            }
        }

        /// <summary>
        /// Tests Suspend method.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ObjectDisposedException))]
        public void SuspendTest3()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            EventWatcher target;
            using (target = new EventWatcher(x))
            {
            }
            target.Suspend();
        }

        /// <summary>
        /// Tests Resume method.
        /// </summary>
        [TestMethod]
        public void ResumeTest1()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            using (EventWatcher target = new EventWatcher(x))
            {
                int eventCount = 0;
                target.EventOccurred += delegate(object sender, EventWatcherEventArgs e)
                {
                    eventCount++;
                };

                x.RaiseFourEvents();
                target.Suspend();
                x.RaiseFourEvents();
                target.Resume();
                x.RaiseFourEvents();

                Assert.AreEqual(8, eventCount, "Invalid number of events.");
            }                        
        }

        /// <summary>
        /// Tests Resume method.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void ResumeTest2()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            using (EventWatcher target = new EventWatcher(x))
            {
                target.Resume();
            }
        }

        /// <summary>
        /// Tests Resume method.
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ObjectDisposedException))]
        public void ResumeTest4()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            EventWatcher target;
            using (target = new EventWatcher(x))
            {
            }
            target.Resume();
        }

        /// <summary>
        /// Tests Dispose method.
        /// </summary>
        [TestMethod]
        public void DisposeTest1()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            int eventCount = 0;

            using (EventWatcher target = new EventWatcher(x))
            {
                target.EventOccurred += delegate(object sender, EventWatcherEventArgs e)
                {
                    eventCount++;
                };
                x.RaiseFourEvents();
            }
            
            x.RaiseFourEvents();

            Assert.AreEqual(4, eventCount, "Invalid number of events.");            
        }

        /// <summary>
        /// Tests Dispose method.
        /// </summary>
        [TestMethod]
        public void DisposeTest2()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();
            EventWatcher target = new EventWatcher(x);
            x.RaiseFourEvents();
            target.Dispose();
            target.Dispose();
        }

        [TestMethod]
        public void Demo1()
        {
            SomeClassThatRaisesEvents x = new SomeClassThatRaisesEvents();

            using (EventWatcher target = new EventWatcher(x))
            {
                target.EventOccurred += delegate(object sender, EventWatcherEventArgs e)
                {
                    Debug.WriteLine(string.Format("EventOccurred: {0}", e.Name));
                };
                x.RaiseFourEvents();
            }
        }

        [TestMethod]
        [Ignore]
        public void Demo2()
        {
            Form form = new Form { Size = new Size(200, 200), Location = new Point(0, 0) };

            using (EventWatcher target = new EventWatcher(form))
            {
                form.ShowDialog();

                EventWatcherResults results = target.GetResults();
            }
        }

    }
}
